// -*- C++ -*-
//===----------------------------------------------------------------------===//
// Copyright 2016 ARM Limited. All rights reserved
//===----------------------------------------------------------------------===//

#ifndef __EXTERNAL_THREADING
#define __EXTERNAL_THREADING

#include <__config>
#include <chrono>
#include <cerrno>
#include <arm-tpl.h>

_LIBCPP_BEGIN_NAMESPACE_STD

#define _LIBCPP_THREAD_ABI_VISIBILITY inline _LIBCPP_INLINE_VISIBILITY

// Clocks

typedef ::__ARM_TPL_timespec_t __libcpp_timespec_t;

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_clock_realtime(__libcpp_timespec_t* ts) {
    return ::__ARM_TPL_clock_realtime(ts);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_clock_monotonic(__libcpp_timespec_t* ts) {
    return ::__ARM_TPL_clock_monotonic(ts);
}

// Mutex
typedef ::__ARM_TPL_mutex_t __libcpp_mutex_t;
typedef ::__ARM_TPL_mutex_t __libcpp_recursive_mutex_t;

#define _LIBCPP_MUTEX_INITIALIZER _ARMCPP_MUTEX_INITIALIZER

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_recursive_mutex_init(__libcpp_recursive_mutex_t* __m) {
    return ::__ARM_TPL_recursive_mutex_init(__m);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_recursive_mutex_lock(__libcpp_recursive_mutex_t *__m) {
    return ::__ARM_TPL_mutex_lock(__m);
}

_LIBCPP_THREAD_ABI_VISIBILITY
bool __libcpp_recursive_mutex_trylock(__libcpp_recursive_mutex_t *__m) {
    return ::__ARM_TPL_mutex_trylock(__m);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_recursive_mutex_unlock(__libcpp_recursive_mutex_t *__m) {
    return ::__ARM_TPL_mutex_unlock(__m);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_recursive_mutex_destroy(__libcpp_recursive_mutex_t *__m) {
    return ::__ARM_TPL_mutex_destroy(__m);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_mutex_lock(__libcpp_mutex_t* __m) {
    return ::__ARM_TPL_mutex_lock(__m);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_mutex_trylock(__libcpp_mutex_t* __m) {
    return ::__ARM_TPL_mutex_trylock(__m);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_mutex_unlock(__libcpp_mutex_t* __m) {
    return ::__ARM_TPL_mutex_unlock(__m);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_mutex_destroy(__libcpp_mutex_t* __m) {
    return ::__ARM_TPL_mutex_destroy(__m);
}

// Condition variable
typedef __ARM_TPL_condvar_t __libcpp_condvar_t;
#define _LIBCPP_CONDVAR_INITIALIZER _ARMCPP_CONDVAR_INITIALIZER

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_condvar_signal(__libcpp_condvar_t* __cv) {
    return ::__ARM_TPL_condvar_signal(__cv);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_condvar_broadcast(__libcpp_condvar_t* __cv) {
    return ::__ARM_TPL_condvar_broadcast(__cv);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_condvar_wait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m) {
    return ::__ARM_TPL_condvar_wait(__cv, __m);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_condvar_timedwait(__libcpp_condvar_t* __cv, __libcpp_mutex_t* __m,
                               __libcpp_timespec_t* __ts) {
    return ::__ARM_TPL_condvar_timedwait(__cv, __m, __ts);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_condvar_destroy(__libcpp_condvar_t* __cv) {
    return ::__ARM_TPL_condvar_destroy(__cv);
}

// Execute once
typedef __ARM_TPL_exec_once_flag __libcpp_exec_once_flag;
#define _LIBCPP_EXEC_ONCE_INITIALIZER _ARMCPP_EXEC_ONCE_INITIALIZER

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_execute_once(__libcpp_exec_once_flag *flag,
                          void (*init_routine)(void)) {
   return ::__ARM_TPL_execute_once(flag, init_routine);
}

// Thread id
typedef __ARM_TPL_thread_id __libcpp_thread_id;

_LIBCPP_THREAD_ABI_VISIBILITY
bool __libcpp_thread_id_equal(__libcpp_thread_id t1, __libcpp_thread_id t2) {
     return ::__ARM_TPL_thread_id_compare(t1, t2) == 0;
}

_LIBCPP_THREAD_ABI_VISIBILITY
bool __libcpp_thread_id_less(__libcpp_thread_id t1, __libcpp_thread_id t2) {
     return ::__ARM_TPL_thread_id_compare(t1, t2) < 0;
}

// Thread
typedef __ARM_TPL_thread_t __libcpp_thread_t;
#define _LIBCPP_NULL_THREAD _ARMCPP_NULL_THREAD

_LIBCPP_THREAD_ABI_VISIBILITY
bool __libcpp_thread_isnull(const __libcpp_thread_t* __t) {
     return __t->data == 0;
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_thread_create(__libcpp_thread_t* __t, void* (*__func)(void*), void* __arg) {
    return ::__ARM_TPL_thread_create(__t, __func, __arg);
}

_LIBCPP_THREAD_ABI_VISIBILITY
__libcpp_thread_id __libcpp_thread_get_current_id() {
    return ::__ARM_TPL_thread_get_current_id();
}

_LIBCPP_THREAD_ABI_VISIBILITY
__libcpp_thread_id __libcpp_thread_get_id(const __libcpp_thread_t* __t) {
    return ::__ARM_TPL_thread_get_id(__t);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_thread_join(__libcpp_thread_t* __t) {
    return ::__ARM_TPL_thread_join(__t);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_thread_detach(__libcpp_thread_t* __t) {
    return ::__ARM_TPL_thread_detach(__t);
}

_LIBCPP_THREAD_ABI_VISIBILITY
void __libcpp_thread_yield() {
    ::__ARM_TPL_thread_yield();
}

_LIBCPP_THREAD_ABI_VISIBILITY
void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns)
{
   using namespace chrono;
   seconds __s = duration_cast<seconds>(__ns);
   __libcpp_timespec_t __ts;
   typedef decltype(__ts.tv_sec) ts_sec;
   _LIBCPP_CONSTEXPR ts_sec __ts_sec_max = numeric_limits<ts_sec>::max();

   if (__s.count() < __ts_sec_max)
   {
     __ts.tv_sec = static_cast<ts_sec>(__s.count());
     __ts.tv_nsec = static_cast<decltype(__ts.tv_nsec)>((__ns - __s).count());
   }
   else
   {
     __ts.tv_sec = __ts_sec_max;
     __ts.tv_nsec = 999999999; // (10^9 - 1)
   }

   while (::__ARM_TPL_thread_nanosleep(&__ts, &__ts) == -1 && errno == EINTR);
}

_LIBCPP_THREAD_ABI_VISIBILITY
unsigned __libcpp_thread_hw_concurrency() {
    return ::__ARM_TPL_thread_hw_concurrency();
}

// Thread local storage
typedef __ARM_TPL_tls_key __libcpp_tls_key;

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_tls_create(__libcpp_tls_key* __key, void (*__at_exit)(void*)) {
    return ::__ARM_TPL_tls_create(__key, __at_exit);
}

_LIBCPP_THREAD_ABI_VISIBILITY
void* __libcpp_tls_get(__libcpp_tls_key __key) {
    return ::__ARM_TPL_tls_get(__key);
}

_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_tls_set(__libcpp_tls_key __key, void* __p) {
    return ::__ARM_TPL_tls_set(__key, __p);
}

#define _LIBCPP_TLS_DESTRUCTOR_CC

_LIBCPP_END_NAMESPACE_STD

#endif // __EXTERNAL_THREADING
